package gamemngr;

import houserules.ArmyExchangeRule;
import houserules.HouseRuleInterface;
import houserules.InitMaxSoldierNum;
import houserules.NumDiceOneMove;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.InputMismatchException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;

import logic.Continent;
import logic.Country;
import logic.GameMap;
import logic.Mission;
import logic.Missions;
import logic.Player;
import logic.RecordTable;
import logic.moves.AddSoldierMove;
import logic.moves.AttackMove;
import logic.moves.ExchangeMove;
import logic.moves.GameMove;
import logic.moves.SettleMove;
import logmngr.LogMngr;
import resources.Configuration;

public class GameMngr implements Serializable{

	private static final long serialVersionUID = 1L;

	public enum GameStatus
	{
		NO_GAME,
		DIVIDE_SOLDIERS,
		GAME_PLAY,
		MOVE_SOLDIERS
	}
	
	//=== Fields ===
	private boolean 		gameOver;
	private boolean			commited;
	private HelpMenu 		helpMenu;
	private GameMap 		map;
	private List<Player> 	players;
	private Missions 		missions;
	private int				uid = 0;
	private	Scanner			scanner;
	private int             numMovesRule = -1; //number of moves allowed each turn
	private int             maxReenforcement = Integer.MAX_VALUE; //max number of soldiers every player gets every turn
	private boolean         limitMove = true; //limitation on the number of move phase
	public	GameStatus		status = GameStatus.NO_GAME;


	private ArmyExchangeRule army_rule = new ArmyExchangeRule();
	private InitMaxSoldierNum map_rule = new InitMaxSoldierNum();
	private NumDiceOneMove players_rule = new NumDiceOneMove();

	public static RecordTable recordTable = new RecordTable();
	private boolean isLoad = false;
	private int gameID;

	//=== Constructor ===
	public GameMngr()
	{
		map = new GameMap();
		players = new LinkedList<Player>();	
		new File(Configuration.saveDir).mkdirs();
	}
	
	public GameMngr(int gameID){
		this();
		this.gameID = gameID;
	}

	//=== Methods ===

	/**
	 * @param map the map to set
	 */
	public void setMap(GameMap map) {
		this.map = map;
	}

	/**
	 * @param players the players to set
	 */
	public void setPlayers(List<Player> players) {
		this.players = players;
	}

	/**
	 * This method is used to configure before game starts
	 * different game parameters such as the Help menu
	 * and other game rules configuration that will be stored 
	 * temporarily in a file. 
	 */
	public void initFromFile(String helpMenuPath)
	{
		//File file = null;

		if(helpMenuPath != null && helpMenuPath.length() > 0)
			helpMenu = new HelpMenu(helpMenuPath);

		/*if(file == null || !file.exists())
			LogMngr.getLogger().writeLog(LogType.Warn, "file: " + helpMenuPath + "doesn't exist");
		else if(!file.canRead())
			LogMngr.getLogger().writeLog(LogType.Warn, "file: " + helpMenuPath + "cannot read");
		else
			helpMenu = new HelpMenu(file);*/
	}
	/**
	 * Delete all players (in case of name problem for example)
	 * @return true iff players list equal to null
	 */
	public boolean delPlayers(){
		players = new LinkedList<Player>();
		return true;
	}

	/**
	 * Initializes all pre game configuration from different sources
	 * Currently supports help menu from file.
	 * Path to file is either specified as first argument
	 * or will be defaulted to help.txt 
	 */
	private void init(String[] args)
	{
		if(args.length > 0 && args[0].length() > 0)
			initFromFile(args[0]);
		else
			initFromFile("help.txt");

		scanner = new Scanner(System.in);
	}

	/**
	 * 
	 * @param args
	 * @return true if correctly passed arguments
	 * false and log error if not correct use.
	 * Currently no default usage has been specified
	 */
	private static boolean validateUsage(String[] args)
	{
		return true;
	}

	/**
	 * 
	 * @param br
	 * BufferedReader to read from System.in 
	 * game players and instantiate the player list
	 */
	private void getPlayersFromConsole()
	{
		boolean retry = true;
		String playerName;
		int playerNum = 0;
		// Get number of players
		System.out.print("Please enter number of players, Only 2-6 players allowed: ");
		playerNum = GameMngr.getInt(scanner, 2, 7);

		retry = true; // reset

		// Get playerNum players and add to game
		for(int pIdx = 0; pIdx < playerNum; pIdx++)
		{
			retry = true;
			while(retry)
			{
				System.out.print("Please enter name of player " + (pIdx + 1) +": ");
				if(scanner.hasNext())
				{
					retry = false;
					playerName = scanner.next();
					if(playerExists(playerName)){
						System.out.println("Name already taken, please insert another name");
						retry = true;
						continue;
					}
					Player newPlayer = new Player(playerName, this);
					System.out.println("is human player? (y/n)");
					if(!GameMngr.getBool(scanner)) {
						newPlayer.markAsComputer();
						System.out.println("Please enter level of proficency (1. Beginner 2. Expert)");
						int level = GameMngr.getInt(scanner, 1, 3);
						newPlayer.setLevel(level);
					}
					players.add(newPlayer);

				}	
			}
		}

		System.out.println();

		giveMissionsToPlayers();

		System.out.println();
	}

	/**
	 * give the players their missions
	 */
	public void giveMissionsToPlayers() {
		// set missions		
		missions = new Missions(map, players);
		for(Player player: players) {
			Mission mission;
			mission = missions.getMission(player);
			player.setSecretMission(mission);
			if(player.isComputer())
				player.initStrategy(map);
		}
	}


	/**
	 * 
	 * @param playerName
	 * @return true iff player with the same name already exists
	 */
	public boolean playerExists(String playerName)
	{
		for(Player player: players)
			if(player.getPlayerName().compareTo(playerName) == 0)
				return true;
		return false;
	}


	/**
	 * Divide countries evenly between players
	 */
	public void divideCountries(){
		int numOfPlayers = this.players.size();
		int numOfCountries = this.map.getCountries().size();
		boolean[] countryIndices = new boolean[numOfCountries];
		for(int i=0; i<countryIndices.length; i++){
			countryIndices[i] = false;
		}
		int countriesPerPlayer = numOfCountries/numOfPlayers;
		Collection<Country> countries = this.map.getCountries().values();
		Random random = new Random();
		for(Player player : this.players){
			for(int i=0; i<countriesPerPlayer; i++){
				int size = countries.size();
				int n = 0;
				do{
					n = random.nextInt(size);
				}while(countryIndices[n] == true);
				countryIndices[n] = true;
				Iterator<Country> iterator = countries.iterator();
				Country country = null;
				for(int j=0; j<=n; j++){
					country = iterator.next();
				}
				SettleMove settle = new SettleMove(country, player);
				settle.makeMove();
			}
		}
		int size = this.players.size();
		boolean[] arr = new boolean[size];
		for(int i=0; i<arr.length; i++){
			arr[i] = false;
		}
		for(Country country : countries){
			if(!country.hasOwner()){
				int n=0;
				do{
					n = random.nextInt(size);
				}while(arr[n]);
				Player player = this.players.get(n);
				SettleMove settle = new SettleMove(country, player);
				settle.makeMove();
			}
		}
	}

	/**
	 * All players gets the option to add soldiers to add before the game begins  
	 */
	public void divideSoldiers() {
		int numOfPlayers = this.players.size();
		for(int i=0; i<numOfPlayers; i++) {
			Player curr = players.get(i);
			setReenforcement(curr);
			if(curr.isComputer())
			{
				while(curr.getSoldiers() > 0)
				{
					GameMove addMove = curr.getNextMove();
					curr.addMove(addMove, false);
				}
			}
			else
			{
				while (curr.getSoldiers() > 0) {
					System.out.println(this.players.get(i).getPlayerName() + ", Do you want to add soliders to your countries? (yes/no)");
					if(GameMngr.getBool(scanner)){
						uid = i;
						executeCommand("add", uid);
					} else {
						break;
					}
				}
			}
			curr.commitMoves();
		}
		uid = 0;
	}

	/**
	 * 
	 * @param reader
	 */
	private void dispatchGameLoop()
	{
		String cmd = null;
		boolean firstReenforce = true;
		if(!this.isLoad){
			divideCountries();		
			divideSoldiers();
		}
		while(!gameOver)
		{
			commited = false;
			// set reinforcements only after first turn of all players is done
			if (!firstReenforce)
				setReenforcement(players.get(uid));
			System.out.println(players.get(uid).getPlayerName() + ", Make your moves");
			System.out.println("Remember: Enter commit to make your moves");
			while(!commited && !gameOver)
			{
				if (players.get(uid).isComputer()) {
					while (players.get(uid).hasNextMove())
					{
						GameMove move = players.get(uid).getNextMove();
						players.get(uid).addMove(move, false);
					}
					players.get(uid).commitMoves();
					if(players.get(uid).isMissionAccomplished())
					{
						System.out.println(players.get(uid).getPlayerName() + " won the game!");
						gameOver = true;
						commited = true;
						System.out.println("Game has ended.");
					}
					commited = true;
					break;
				}
				System.out.print("Enter command: ");
				cmd = GameMngr.getLine(scanner);
				executeCommand(cmd, uid);
				if (numMovesRule == players.get(uid).getMoves().size()) { 
					commited = true;
					numMovesRule = -1;
				}
				System.out.println();
			}
			if(uid == players.size())
				firstReenforce = false;
			if(!gameOver)
				advancePlayerTurn();
		}
	}

	/**
	 * engage a war on a country
	 * @param attackingCountry
	 * @param defendingCountry
	 * @param uid
	 */
	public void engageWar(Country attackingCountry, Country defendingCountry, int uid){
		if (attackingCountry.getSoldiers()<2) {
			System.err.println("You cannot attack with only one soldier");
			return;
		}
		AttackMove attack = new AttackMove(attackingCountry, defendingCountry);
		players.get(uid).addMove(attack, false);
		boolean retreat = false;
		while(!attack.isBattleFinished() && !retreat){
			if (attackingCountry.getSoldiers()<2) {
				System.err.println("You cannot attack with only one soldier");
				return;
			}
			System.out.println("Continue attack (yes/no)? ");
			retreat = !GameMngr.getBool(scanner);
			if(!retreat)
			{
				attack = new AttackMove(attackingCountry, defendingCountry);
				players.get(uid).addMove(attack, false);
			}
		}

		if(!attack.isAttackWon()){
			System.out.println(players.get(uid).getPlayerName() + " has lost the war!");
		}
		else {
			System.out.println("How many soldiers do you want to move to " + defendingCountry.toString() + "?");
			int numSol = getInt(scanner, Math.min(attackingCountry.getSoldiers()-1, 3), attackingCountry.getSoldiers());
			attack.setMoveSoldiers(numSol);
			attack.moveSoldiersOnConquer(false, this);
			
		}
	}

	/**
	 * executes the command cmd with player uid (user id)s
	 **/
	public void executeCommand(String cmd, int uid) {

		String[] cmdArr = cmd.split(" ");

		if(cmdArr[0].equals("exit")){
			gameOver = true;
			commited = true;
			System.out.println("Game has ended.");
		}else if(cmdArr[0].equals("help")){
			helpMenu.printHelp();
		}else if(cmdArr[0].equals("map")){
			System.out.print(map.toString());
			//Command is: settle <COUNTRY NAME>
		}else if(cmdArr[0].equals("settle")){
			Country country = getSettleCountryByIndex();
			if(country != null){
				GameMove settle = new SettleMove(country, players.get(uid));
				players.get(uid).addMove(settle, false);
				// only one settle move per player in game iteration
				commited = this.players.get(uid).commitMoves();
			}
			else{
				System.out.println("Failed to settle");
			}
		}else if(cmdArr[0].compareToIgnoreCase("attack") == 0){
			System.out.println("Choose a country to attack with:");
			Country attackingCountry = getPlayerCountries(players.get(uid), true, null);
			System.out.println("Choose the country to attack:");
			Country defendingCountry = getPlayerCountries(players.get(uid), false, attackingCountry);
			if((attackingCountry != null) && (defendingCountry != null) && (Country.isNeighbors(attackingCountry, defendingCountry))){
				engageWar(attackingCountry, defendingCountry, uid);
			} else if(defendingCountry == null){
				System.out.println(attackingCountry.getName() + " doesn't have neighbors to attack.");
			} else{
				System.err.println("Failed to attack" + defendingCountry);
			}
		}
		// Command is: commit
		// Commit all players moves and go to the next player
		else if(cmdArr[0].compareToIgnoreCase("commit") == 0)
		{
			commited = this.players.get(uid).commitMoves();
			// Command is: undo
			// undoing the player's last move
		} else if(cmdArr[0].compareToIgnoreCase("undo") == 0){
			if(players.get(uid).hasMoves()) {
				if (!players.get(uid).undoMove(false)) {
					System.out.println("You cannot undo your last move");
				}
			}
			else
				System.out.println("No moves to undo");

			//command is: redo
			// redoing the last player's move
		} else if(cmdArr[0].compareToIgnoreCase("redo") == 0){
			if(players.get(uid).hasRedoMoves())
				players.get(uid).redoMove(false);
			else
				System.out.println("No moves to redo");

		}  else if(cmdArr[0].compareToIgnoreCase("save") == 0){
			saveFromConsoleGame();
			System.out.println("saving...");
			System.out.println("saved");
		} 
		else if(cmdArr[0].compareToIgnoreCase("move") == 0) {
			if (limitMove) {
				System.out.println("Are you sure you wish to move soldiers? This will be your last move (y/n)");
				if (!getBool(scanner))
					return;
			}
			System.out.println("Choose from which country do you wish to move soldiers:");
			Country from = getPlayerCountries(this.players.get(uid) ,true, null);
			System.out.println("Choose to which country do you wish to move your soldiers:");
			Country to = getSelection(from);
			System.out.println("Enter amount of soldiers you wish to move");
			int amount = getInt(scanner);
			if (from.getSoldiers()-amount<1) {
				System.err.println("You have to leave at least 1 soldier in " + from.toString());
				System.err.println("If you still want to move soldiers, please try again.");
			}
			else {
				ExchangeMove exchange = new ExchangeMove(from, to, amount);
				players.get(uid).addMove(exchange, false);
				if (limitMove) 
					commited = true;
			}
		}		
		else if(cmdArr[0].compareToIgnoreCase("records") == 0){
			System.out.println(GameMngr.recordTable);
		} else if(cmdArr[0].compareToIgnoreCase("amount") == 0){
			System.out.println("You have " + players.get(uid).getSoldiers() + " soldiers");
		} else if(cmdArr[0].compareToIgnoreCase("declare") == 0){
			if (players.get(uid).isMissionAccomplished()) {
				System.out.println(players.get(uid).getPlayerName() + " won the game!");
				gameOver = true;
				commited = true;
				System.out.println("Game has ended.");
			} else System.out.println("There's still no winner. You better try harder..");
		} else if(cmdArr[0].compareToIgnoreCase("add") == 0){
			if(players.get(uid).getSoldiers() > 0){
				System.out.println("Insert the country you want to add soldiers: ");
				Country country = getPlayerCountries(players.get(uid), true, null);
				System.out.print("Insert the amount of soldiers you want to add: ");
				int amount = GameMngr.getInt(scanner, 1, players.get(uid).getSoldiers() + 1);
				if(country != null){
					GameMove add = new AddSoldierMove(players.get(uid), country, amount);
					players.get(uid).addMove(add, false);
				}
				else{
					System.err.println("Failed to add soldiers in " + country.getName());
				}
			}
			else
			{
				System.out.println("you dont have soldiers to add!");
			}
		} else if(cmdArr[0].trim().equals("mission")){
			System.out.print(players.get(uid).getPlayerName() + ", you ");
			System.out.println(players.get(uid).getSecretMission().getMission());
		} else if(cmdArr[0].trim().equals("")){
			return;
		}
		else {
			System.err.println("Illeagal command entered");
		}
	}

	/**
	 * @param from
	 * @return the country chosen by the player to move soldiers to
	 */
	private Country getSelection(Country from) {

		Set<Country> countries = new HashSet<Country>();
		Player owner = from.getOwner();
		from.getConnectedCountries(countries, owner);
		countries.remove(from);
		Country[] arr = countries.toArray(new Country[countries.size()]);
		for (int i=0; i<arr.length; i++) {
			System.out.println(i + ". " + arr[i]);
		}
		System.out.println("Make your selection: ");
		int index = GameMngr.getInt(scanner, 0, arr.length);
		return arr[index];
	}
	
	private Country getSettleCountryByIndex() {
		Country[] countries = map.getUnsettledCountries();
		if(countries.length == 0){
			System.out.println("There are no unsettled countries!");
			return null;
		}
		for(int i=0; i<countries.length; i++){
			System.out.println(i + ". " + countries[i].getName());
		}
		System.out.println("Choose a the country's number:");
		int index = GameMngr.getInt(scanner, 0, countries.length);
		return countries[index];
	}

	private Country getPlayerCountries(Player player, boolean mine, Country country){
		Country[] countries = null;
		if(country == null){
			countries = this.map.getPlyersCountries(player, mine);
		} else {
			countries = this.map.getPlyersCountries(player, mine, country);
		}
		if(countries.length == 0){
			return null;
		}
		for(int i=0; i<countries.length; i++){
			System.out.println(i + ". " + countries[i].getName());
		}
		System.out.print("Enter the country's number: ");
		int index = GameMngr.getInt(scanner, 0, countries.length);
		return countries[index];
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		LogMngr.getLogger().init();

		if(!validateUsage(args))
			return;

		GameMngr game = new GameMngr();
		game.init(args);

		//game load
		game.isLoad = game.isLoadGame();

		// Get Player number and names
		if (!game.isLoad) {
			game.getPlayersFromConsole();
		}

		// some valid checks
		if(game.players.size() == 0)
			return;

		// ================== set house rules =======================

		// ask user to enter house rules or to use default 
		boolean house_rules = game.isSetHouseRules();

		if (house_rules){
			game.changeMoves();
			game.changeReenforcement();
			game.changeMoveLimit();
		}
		//run the game
		game.dispatchGameLoop();
		// close the game
		game.destroy();
	}

	/*
	 * Gives the player the option to move soldiers more then once each turn 
	 */
	private void changeMoveLimit() {
		System.out.println("Do you want to be able to move soldiers more then once each turn? (y/n)");
		limitMove = !getBool(scanner);  
	}
	
	/**
	 * returns true if the number of move phases is limited (new house rule)
	 * @return - true for limited, otherwise false.
	 */
	public boolean getLimitMove() {
		return limitMove;
	}
	
	public void setLimitMove(boolean value) {
		limitMove = value;
	}

	/*
	 * Gives the player the option to limit reenforcement
	 */
	private void changeReenforcement() {
		System.out.println("Do you wish to limit the size of the reenforcement? (y/n)");
		if (getBool(scanner)) {
			System.out.println("What should be the maximum size of the reenforcement? (1-100)");
			maxReenforcement = getInt(scanner, 1, 100);
		}
	}
	
	/**
	 * Set the maxReenforcement number to be a given number.
	 * @param num - the new value of the maxReenforcement
	 */
	public void setMaxReenforcement(int num) {
		maxReenforcement = num;
	}
	
	/**
	 * @return the maximum number of soldiers that you can add in each turn - House Rule
	 */
	public int getMaxReenforcement() {
		return maxReenforcement;
	}

	/*
	 * Giving the player the option to limit the number of moves each turn
	 */
	private void changeMoves() {
		System.out.println("Do you wish to limit number of moves each turn? (y/n)");
		if (getBool(scanner)) {
			System.out.println("How many moves do you wish to allow? (1-100)");
			numMovesRule = getInt(scanner, 1, 101); 
		}
	}
	/**
	 * Set the numMovesRule number to be a given number.
	 * @param num - the new value of the numMovesRule
	 */
	public void setNumMoveRule(int num) {
		numMovesRule = num;
	}
	
	public int getNumMoveRule() {
		return numMovesRule;
	}

	/**
	 * Free all resources used by GameMngr
	 */
	private void destroy() {
		scanner.close();
	}

	/**
	 * Ask at the beginning of the game to set house rules 
	 */
	private boolean isSetHouseRules() {
		while (true){
			System.out.print("Do you want to change house rules (yes\\no for default)? ");
			return GameMngr.getBool(scanner);
		}
	}

	private boolean isLoadGame() {
		while (true){
			System.out.print("Do you want to load previous game (yes\\no)? ");
			String answer = scanner.next();
			if (answer.equalsIgnoreCase("yes")) {
				return loadGameFromConsole();
			}
			else if (answer.equalsIgnoreCase("no")) {
				return false;
			}
			else {
				System.out.println("Illegal answer entered. Only yes/no answers allowed.");
			}
		}
	}

	/**
	 * @return the gameOver
	 */
	public boolean isGameOver() {
		return gameOver;
	}

	/**
	 * Ends the game
	 */
	public void setGameOver() {
		gameOver = true;
	}

	/**
	 * @return the committed
	 */
	public boolean isCommited() {
		return commited;
	}

	/**
	 * Change the value of commited to the given value.
	 * @param value - boolean that indicates if changes were commited.
	 */
	public void setCommited(boolean value) {
		commited = value;
	}

	/**
	 * @return the helpMenu
	 */
	public HelpMenu getHelpMenu() {
		return helpMenu;
	}

	/**
	 * @return the map
	 */
	public GameMap getMap() {
		return map;
	}

	/**
	 * @return the players 
	 */
	public List<Player> getPlayers() {
		return players;
	}

	public void addPlayer(Player player){
		this.players.add(player);
	}

	/**
	 * saving the game to the file argument
	 */
	public boolean saveGame(File file){
		new File(Configuration.saveDir).mkdir();
		try {
			FileOutputStream fos = new FileOutputStream(file);
			ObjectOutputStream oos =  new ObjectOutputStream(fos);
			oos.writeObject(this);
			oos.close();
		} catch (FileNotFoundException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
		return true;
	}

	/**
	 * saving the game status into a file from the console
	 */
	public void saveFromConsoleGame(){
		Scanner scanner = new Scanner(System.in);
		System.out.print("Insert save name: ");
		File file = new File(Configuration.saveDir + File.separator + scanner.nextLine() + ".data");
		saveGame(file);
	}

	/**
	 * load the game from the argument file
	 * @param file
	 * @return true iff the game has been loaded.
	 */
	public GameMngr loadGame(File file){
		GameMngr game;
		try {
			FileInputStream fos = new FileInputStream(file);
			ObjectInputStream ois =  new ObjectInputStream(fos);
			try {
				game = (GameMngr)ois.readObject();
			} catch (ClassNotFoundException e) {
				return null;
			}
			ois.close();
		} catch (FileNotFoundException e) {
			return null;
		} catch (IOException e) {
			return null;
		}
		return game;
	}

	/**
	 * loads a GameMngr instance saved on a file
	 * @return
	 */
	public boolean loadGameFromConsole() {
		Scanner scanner = new Scanner(System.in);
		if(printSavedGames()){
			System.out.print("Insert game name: ");
		} else {
			return false;
		}
		File file = new File(Configuration.homeDir + File.separator + scanner.nextLine() + ".data");
		if(!file.exists()){
			System.err.println("You enterded a worng file name.");
			return false;
		}
		GameMngr tempGame = loadGame(file);
		if(tempGame == null){
			return false;
		} else {
			copyFields(tempGame);
			return true;
		}
	}

	/**
	 * copies the fields of the game loaded to this' fields
	 * @param tempGame
	 */
	private void copyFields(GameMngr tempGame) {
		this.gameOver = tempGame.gameOver;
		this.commited = tempGame.commited;
		this.helpMenu = tempGame.helpMenu;
		this.map = tempGame.map;
		this.players = tempGame.players;
		this.missions = tempGame.missions;
		this.uid = tempGame.uid;
		this.scanner = tempGame.scanner;
		this.numMovesRule = tempGame.numMovesRule;
		this.maxReenforcement = tempGame.maxReenforcement;
		this.limitMove = tempGame.limitMove;
		this.status = tempGame.status;
		this.army_rule = tempGame.army_rule;
		this.map_rule = tempGame.map_rule;
		this.players_rule = tempGame.players_rule;
		this.isLoad = tempGame.isLoad;
		
	}

	public List<HouseRuleInterface> getHouseRules(){
		List<HouseRuleInterface> temp = new LinkedList<HouseRuleInterface>();
		temp.add(this.army_rule);
		temp.add(this.map_rule);
		temp.add(this.players_rule);
		return temp;
	}

	/**
	 * returns an array of saved files string names
	 */
	public String[] getSavedGames(){
		Set<String> savedGames = null;
		File file = new File(Configuration.saveDir);
		if(!file.exists()){
			return null;
		} else {
			savedGames = new HashSet<String>();
			for(File saveFile : file.listFiles()){
				if((!saveFile.isHidden()) && (!saveFile.isDirectory())){
					StringBuffer sb = new StringBuffer(saveFile.getName());
					String saveName = sb.substring(0, saveFile.getName().lastIndexOf('.')).trim();
					if(!saveName.equals(""))
						savedGames.add(saveName);
				}
			}
		}
		return savedGames.toArray(new String[savedGames.size()]);
	}

	public boolean printSavedGames(){
		System.out.println("The saved games are: ");
		String[] saved = this.getSavedGames();
		if(saved == null){
			System.out.println("Threre are no saved games");
			return false;
		} else {
			for(String str : saved){
				System.out.println(str);
			}
		}
		return true;
	}

	public void setReenforcement(Player player) {
		int amount = Math.max((player.getCountries().size())/3, 3);
		for (Continent continent: this.map.getContinents().values()) {
			if (continent.isOwnedBy(player)) {
				if (continent.getName().equalsIgnoreCase("Asia"))
					amount+=7;
				else if (continent.getName().equalsIgnoreCase("North America"))
					amount+=5;
				else if (continent.getName().equalsIgnoreCase("Europe"))
					amount+=5;
				else if (continent.getName().equalsIgnoreCase("Africa"))
					amount+=3;
				else if (continent.getName().equalsIgnoreCase("South America"))
					amount+=2;
				else if (continent.getName().equalsIgnoreCase("Australia"))
					amount+=2;
			}
		}
		amount = Math.min(amount, maxReenforcement);
		System.out.println(player.getPlayerName() + ", you got " + amount + " more soldiers.");
		player.setFreeSolidersAmount(player.getSoldiers() + amount);
	}

	/**
	 * 
	 * @param scanner
	 * @return integer typed in console
	 * @SAFE function that validates user input
	 */
	public static int getInt(Scanner scanner)
	{	
		int res = 0;
		boolean retry = true;
		while(retry)
		{
			try
			{
				String input = GameMngr.getLine(scanner);
				res = Integer.parseInt(input);
				// if no exception, don't retry
				retry = false;
			}
			catch(NumberFormatException nmfex)
			{
				System.out.println("Please insert a valid number!");
			}
			catch(InputMismatchException inmsmtex)
			{
				System.out.println("Please insert a valid number!");
			}
		}
		return res;
	}
	/**
	 * 
	 * @param scanner
	 * @return integer typed in console, range of integer is between min (inclusive) and max (exclusive)
	 * @SAFE function that validates user input, and checks for correct range
	 */
	public static int getInt(Scanner scanner, int min /* inclusive*/, int max /* exclusive*/)
	{
		int res = 0;
		boolean retry = true;
		while(retry)
		{
			res = getInt(scanner);
			if(res < min || res >= max )
			{
				System.out.println("Integer should be between " + min + " and " + (max - 1) + " included");
			}
			else
				retry = false;
		}
		return res;
	}

	/**
	 * 
	 * @param scanner
	 * @return Boolean indicating a yes\no or y\n (ignore case) answer from console
	 * @SAFE Checks user input against exceptions
	 */
	public static boolean getBool(Scanner scanner)
	{
		boolean rc = false;
		boolean retry = true;
		while(retry)
		{
			try
			{
				String input = GameMngr.getLine(scanner);
				if (input.equalsIgnoreCase("y") || input.equalsIgnoreCase("yes") )
					rc = true;
				else if (input.equalsIgnoreCase("n") || input.equalsIgnoreCase("no") )
					rc = false;
				else
				{
					System.out.println("Please insert y\\n or yes\\no");
					retry = true;
					continue;
				}
				retry = false;
			}
			catch(NoSuchElementException nselex)
			{
				System.out.println("Please insert y\\n or yes\\no");
			}
		}
		return rc;	
	}

	public static String getLine(Scanner scanner)
	{
		String input = null;
		// loop to fix a strange bug in scanner, returns delimiter \r\n on first use..
		while(input == null)
		{
			if(scanner.hasNextLine())
				input = scanner.nextLine();
			if(input == null || input.length() == 0)
			{
				input = null; // nullify input so you keep waiting in hasNextLine()
			}
		}
		return input;
	}

	/**
	 * @return the player whose turn is now
	 */
	public Player getCurrentPlayer(){
		return this.players.get(uid);
	}
	
	/**
	 * @return the current player uid
	 */
	public int getCurrentPlayerUid() {
		return uid;
	}
	
	/**
	 * Advances turn of current player but doesn't commit moves
	 */
	public void advancePlayerTurn()
	{
		uid = (++uid) % players.size();
		this.setReenforcement(this.players.get(uid));
	}
	
	/**
	 * 
	 */
	public void commit() {
		getCurrentPlayer().commitMoves();
		switch (status) {
		case GAME_PLAY:
			status = GameStatus.GAME_PLAY;
			break;
		case DIVIDE_SOLDIERS:
			if(getCurrentPlayerUid() == players.size() - 1)
				status = GameStatus.GAME_PLAY;
			else
				status = GameStatus.DIVIDE_SOLDIERS;
			break;
		case MOVE_SOLDIERS:
			status = GameStatus.GAME_PLAY;
			break;
		}
	}

	/**
	 * @return the gameID
	 */
	public int getGameID() {
		return gameID;
	}
}

